﻿
namespace EasyNet.FastDFS
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    internal delegate T UseSocket<T>(PooledSocket socket);
    internal delegate void UseSocket(PooledSocket socket);

    internal class ServerPool
    {
        //Expose the socket pools.
        private SocketPool[] hostList;
        private string[] hostArray;
        internal SocketPool[] HostList { get { return hostList; } }

        private Dictionary<uint, SocketPool> hostDictionary;
        private uint[] hostKeys;

        //Internal configuration properties
        private int sendReceiveTimeout = 2000;
        private int connectTimeout = 2000;
        private uint maxPoolSize = 10;
        private uint minPoolSize = 5;
        private TimeSpan socketRecycleAge = TimeSpan.FromMinutes(30);
        internal int SendReceiveTimeout { get { return sendReceiveTimeout; } set { sendReceiveTimeout = value; } }
        internal int ConnectTimeout { get { return connectTimeout; } set { connectTimeout = value; } }
        internal uint MaxPoolSize { get { return maxPoolSize; } set { maxPoolSize = value; } }
        internal uint MinPoolSize { get { return minPoolSize; } set { minPoolSize = value; } }
        internal TimeSpan SocketRecycleAge { get { return socketRecycleAge; } set { socketRecycleAge = value; } }

        internal ServerPool(string[] hosts)
        {
            hostArray = new string[hosts.Length];
            hostDictionary = new Dictionary<uint, SocketPool>();
            var pools = new List<SocketPool>();
            var keys = new List<uint>();
            var j = 0;
            foreach (string host in hosts)
            {
                hostArray[j] = host;
                var pool = new SocketPool(this, host.Trim());

                for (int i = 0; i < 250; i++)
                {
                    uint key = BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(Encoding.UTF8.GetBytes(host + "-" + i)), 0);
                    if (!hostDictionary.ContainsKey(key))
                    {
                        hostDictionary[key] = pool;
                        keys.Add(key);
                    }
                }

                pools.Add(pool);

            }

            hostList = pools.ToArray();

            keys.Sort();
            hostKeys = keys.ToArray();
        }

        internal SocketPool GetSocketPool(uint hash)
        {
            if (hostList.Length == 1)
            {
                return hostList[0];
            }
            int i = Array.BinarySearch(hostKeys, hash);

            if (i < 0)
            {
                i = ~i;

                if (i >= hostKeys.Length)
                {
                    i = 0;
                }
            }
            return hostDictionary[hostKeys[i]];
        }

        internal SocketPool GetSocketPool(string host)
        {
            return Array.Find(HostList, delegate(SocketPool socketPool) { return socketPool.Host == host; });
        }

        internal SocketPool GetRandmonSocketPool()
        {
            var tick = DateTime.Now.Ticks;
            var random = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));
            var host = hostArray[random.Next(0, hostArray.Length - 1)];

            return Array.Find(HostList, delegate(SocketPool socketPool)
            {
                return socketPool.Host == host;
            });
        }

        internal T Execute<T>(uint hash, T defaultValue, UseSocket<T> use)
        {
            return Execute(GetSocketPool(hash), defaultValue, use);
        }

        internal T Execute<T>(SocketPool pool, T defaultValue, UseSocket<T> use)
        {
            PooledSocket sock = null;

            try
            {
                sock = pool.Acquire();

                if (sock != null)
                {
                    return use(sock);
                }
            }
            catch (Exception ex)
            {
                if (sock != null)
                {
                    sock.Close();
                }
            }
            finally
            {
                if (sock != null)
                {
                    sock.Dispose();
                }
            }
            return defaultValue;
        }

        internal void Execute(SocketPool pool, UseSocket use)
        {
            PooledSocket sock = null;

            try
            {

                sock = pool.Acquire();

                if (sock != null)
                {
                    use(sock);
                }
            }
            catch (Exception)
            {
                if (sock != null)
                {
                    sock.Close();
                }
            }
            finally
            {
                if (sock != null)
                {
                    sock.Dispose();
                }
            }
        }
    }
}
